My First Foray into Full-Stack with webforJ

As I went through my computer science education at Oregon State, I realized pretty quickly that I enjoyed the UI side of things. Messing around with CSS (yes, even trying to center a div) and making pages that looked nice appealed to me way more than databases and business logic.
Somehow, I ended up working at a company that primarily used Java. Lucky for me, the project I ended up on was a Java web framework - back in my comfort zone!
I've been able to keep myself pretty much in UI land since then, in my comfort zone and happy to let my colleagues deal with all that back end stuff.
Recently our framework, webforJ, released Spring integration, and with that, my blissful isolation in UI land came to an end. As my first foray into full-stack development, I was asked to build a (very, very simple) CRUD application using Spring and webforJ both so I could learn more about the back end, and also showcase webforJ and Spring together in one project.
TLDR: It went well.

The Mission: A Music Artist Management System
The task seemed simple enough - build an app to manage music artists, fairly basic stuff:
- Show artists in a nice-looking table (I can do tables!)
- Let users add, edit, and delete artists using Spring
- Use webforJ's table filtering
- Make it look professional, but nothing over the top (finally, my territory!)
I started off watching a few tutorial videos, and started hearing things like "dependency injection" and "beans" thrown around. Thankfully, I remembered that I'm living in the AI age, and instead started to ask various chat models to help me learn more about Spring.
I'm happy to say, everything was straightforward - I managed to (fairly easily) build a simple CRUD app using Spring Boot and webforJ. For those curious about what I did and in what order, here's the process I followed:
The Back End
Creating my data layer took exactly three files. I've written CSS files longer than my entire backend. Of course, this is a simple app, but with the help of my AI buddy, I was able to not only get these files created, but finally understand the relationship between the different layers and components. My data model/entity defines the structure of my data and maps it to the database, my repository provides an interface for CRUD operations and database queries, and my service layer contains business logic and orchestrates communication between controllers and repositories.
File #1 - The Entity:
@Entity
public class MusicArtist {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Artist name is required")
private String name;
private String genre;
private Boolean isActive = true;
// ... more fields
}
Learning about these annotations, and the work they're doing for me that I didn't have to inplement myself was pretty great. The model was also quite small, but I didn't mind learning via something simple to start.
File #2 - The Repository:
@Repository
public interface MusicArtistRepository extends JpaRepository<MusicArtist, Long> {
// That's it. That's the whole file.
// Spring writes all the CRUD operations for you.
}
When I realized this interface was complete and fully functional, that started to really show me how powerful Spring is.
File #3 - The Service (where you put your business logic):
@Service
public class MusicArtistService {
@Autowired
private MusicArtistRepository repository;
public MusicArtist createArtist(MusicArtist artist) {
if (artist.getIsActive() == null) { //Simple business logic/checks built around my CRUD operations
artist.setIsActive(true);
}
return repository.save(artist);
}
public SpringDataRepository<MusicArtist, Long> getFilterableRepository() {
return new SpringDataRepository<>(repository); //Simple method to make sure my Table can efficiently filter data
}
}
And like that, I had a working backend. No SQL queries or complex steps to learn.
webforJ Integration
Now here's where I thought things might get a little more complex. In my experience, making two frameworks play nice usually involves adapter patterns, configuration nightmares, copious amounts of swearing, etc.
But this was all it took:
@Route("/artists")
public class MusicArtistsView extends Composite<FlexLayout> {
private final MusicArtistService artistService;
public MusicArtistsView(MusicArtistService artistService) {
this.artistService = artistService; // Spring just... provides this
setupMyBeautifulUI();
}
}
That's it. Spring automatically knew to provide my service to webforJ, and then I was back to what made sense to me - UI creation.
Making It Look Decent (My Happy Place)
With the scary backend stuff handled, I could focus on what I love - making things look good. And webforJ made this part genuinely fun.
Renderers
I wanted those trendy Gmail-style avatars with initials to display in my table. There are examples of other renderers on our docs page, but a simple renderer was quite easily accomplished. Here's how it works:
public class ArtistAvatarRenderer extends Renderer<MusicArtist> {
@Override
public String build() {
return /* html */"""
<%
const artist = cell.value;
const name = cell.row.getValue("Name");
const yearFormed = cell.row.getValue("Year Formed");
const isActive = cell.row.getValue("Active");
const initials = name ? name.split(' ').map(word => word.charAt(0)).join('').substring(0, 2).toUpperCase() : '?';
const yearText = yearFormed ? (isActive ? 'Active since ' + yearFormed : 'Formed in ' + yearFormed) : 'Status unknown';
%>
<div part='artist-avatar-renderer'>
<div part='artist-avatar'>
<%= initials %>
</div>
<div part='artist-text'>
<div part='artist-name'><%= name %></div>
<div part='artist-year'><%= yearText %></div>
</div>
</div>
""";
}
}
My Table went from "database dump" to looking like it was built with more intention quite easily. I've spent way too much time with Table renderers, and have been quite enjoying building some cool-looking things with them, like this dashboard project I completed a little while ago.
